import { CreateHandleTo, ICallback, IHandle, RemoveHandle } from "../data/data-handle";


interface IDomHandle {
    (eventObject: DomEventObject): void;
}

class DomEventObject {
    isMouse?: boolean;
    clientX?: number;
    clientY?: number;
    touches?: Array<{ clientX: number, clientY: number }>;
    buttons?: number;
    [key: string]: any;
}

const setEventData = (e: DomEventObject): DomEventObject => {
    let isMouse = e.touches ? false : true;
    let event = e;
    event.isTouch = !isMouse;
    event.isMouse = isMouse;
    return event;
};

class DomEvent {

    private _pointEnterHandles: Array<IHandle<DomEventObject>> = [];
    private _pointMoveHandles: Array<IHandle<DomEventObject>> = [];
    private _pointOutHandles: Array<IHandle<DomEventObject>> = [];
    private _pointDownHandles: Array<IHandle<DomEventObject>> = [];
    private _pointUpHandles: Array<IHandle<DomEventObject>> = [];
    private _pointClickHandles: Array<IHandle<DomEventObject>> = [];
    private _pointWheelHandles: Array<IHandle<DomEventObject>> = [];
    private _leftPointDownHandles: Array<IHandle<DomEventObject>> = [];
    private _leftPointUpHandles: Array<IHandle<DomEventObject>> = [];
    private _leftPointMoveHandles: Array<IHandle<DomEventObject>> = [];
    private _rightPointDownHandles: Array<IHandle<DomEventObject>> = [];
    private _rightPointUpHandles: Array<IHandle<DomEventObject>> = [];
    private _rightPointMoveHandles: Array<IHandle<DomEventObject>> = [];
    private _windowBlurHandles: Array<IHandle<DomEventObject>> = [];

    private _touchStartHandle!: IDomHandle;
    private _touchMoveHandle!: IDomHandle;
    private _touchEndHandle!: IDomHandle;
    private _mouseOverHandle!: IDomHandle;
    private _mouseMoveHandle!: IDomHandle;
    private _mouseOutHandle!: IDomHandle;
    private _mouseDownHandle!: IDomHandle;
    private _mouseUpHandle!: IDomHandle;
    private _clickHandle!: IDomHandle;
    private _wheelHandle!: IDomHandle;
    private _windowBlurHandle!: IDomHandle;

    private _element: HTMLDivElement | Window;
    private _isStart: boolean;

    constructor(element: HTMLDivElement | Window) {
        this._element = element;
        this._isStart = false;
        this.initEventHandler();
    }

    public onPointEnter(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._pointEnterHandles);
    }

    public removePointEnterHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._pointEnterHandles);
    }

    public onPointMove(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._pointMoveHandles);
    }

    public removePointMoveHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._pointMoveHandles);
    }

    public onPointOut(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._pointOutHandles);
    }

    public removePointOutHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._pointOutHandles);
    }

    public onPointDown(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._pointDownHandles);
    }

    public removePointDownHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._pointDownHandles);
    }

    public onPointUp(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._pointUpHandles);
    }

    public removePointUpHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._pointUpHandles);
    }

    public onPointClick(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._pointClickHandles);
    }

    public removePointClickHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._pointClickHandles);
    }

    public onPointWheel(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._pointWheelHandles);
    }

    public removePointWheelHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._pointWheelHandles);
    }

    public onLeftPointDown(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._leftPointDownHandles);
    }

    public removeLeftPointDownHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._leftPointDownHandles);
    }

    public onLeftPointMove(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._leftPointMoveHandles);
    }

    public removeLeftPointMoveHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._leftPointMoveHandles);
    }

    public onLeftPointUp(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._leftPointUpHandles);
    }

    public removeLeftPointUpHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._leftPointUpHandles);
    }

    public onRightPointDown(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._rightPointDownHandles);
    }

    public removeRightPointDownHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._rightPointDownHandles);
    }

    public onRightPointMove(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._rightPointMoveHandles);
    }

    public removeRightPointMoveHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._rightPointMoveHandles);
    }

    public onRightPointUp(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._rightPointUpHandles);
    }

    public removeRightPointUpHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._rightPointUpHandles);
    }

    public onWindowBlur(callback: ICallback<DomEventObject>): IHandle<DomEventObject> {
        return CreateHandleTo<DomEventObject>(callback, this._windowBlurHandles);
    }

    public removeWindowBlurHandle(handle: number | IHandle<DomEventObject>): void {
        RemoveHandle(handle, this._windowBlurHandles);
    }

    public start(): void {
        if (this._isStart) return;
        this._isStart = true;
        this._element.addEventListener("touchstart", this._touchStartHandle, { passive: false });
        this._element.addEventListener("touchmove", this._touchMoveHandle, { passive: false });
        this._element.addEventListener("touchend", this._touchEndHandle, { passive: false });
        this._element.addEventListener("mouseover", this._mouseOverHandle, { passive: false });
        this._element.addEventListener("mousemove", this._mouseMoveHandle, { passive: false });
        this._element.addEventListener("mouseout", this._mouseOutHandle, { passive: false });
        this._element.addEventListener("mousedown", this._mouseDownHandle, { passive: false });
        this._element.addEventListener("mouseup", this._mouseUpHandle, { passive: false });
        this._element.addEventListener("click", this._clickHandle, { passive: false });
        this._element.addEventListener("wheel", this._wheelHandle, { passive: false });
        window.addEventListener("blur", this._windowBlurHandle, { passive: false });
        this._element.addEventListener("touchcancel", this._touchEndHandle, { passive: false });
    }

    public end(): void {
        if (!this._isStart) return;
        this._isStart = false;
        this._element.removeEventListener("touchstart", this._touchStartHandle);
        this._element.removeEventListener("touchmove", this._touchMoveHandle);
        this._element.removeEventListener("touchend", this._touchEndHandle);
        this._element.removeEventListener("mouseover", this._mouseOverHandle);
        this._element.removeEventListener("mousemove", this._mouseMoveHandle);
        this._element.removeEventListener("mouseout", this._mouseOutHandle);
        this._element.removeEventListener("mousedown", this._mouseDownHandle);
        this._element.removeEventListener("mouseup", this._mouseUpHandle);
        this._element.removeEventListener("click", this._clickHandle);
        this._element.removeEventListener("wheel", this._wheelHandle);
        window.removeEventListener("blur", this._windowBlurHandle);
        this._element.removeEventListener('touchcancel', this._touchEndHandle);
    }

    public dispose(): void {
        this.end();
    }

    private initEventHandler(): void {
        this._touchStartHandle = (e: DomEventObject) => {
            const event = setEventData(e);
            event.target._isTouch = true;
            this._pointEnterHandles.forEach(h => h.callback(event));
            this._pointDownHandles.forEach(h => h.callback(event));
            let touchLength = event.touches!.length;
            if (touchLength === 1) {
                this._leftPointDownHandles.forEach(h => h.callback(event));
            } else if (touchLength === 2) {
                this._leftPointUpHandles.forEach(h => h.callback(event));
                this._rightPointDownHandles.forEach(h => h.callback(event));
            } else if (touchLength === 3) {
                this._rightPointUpHandles.forEach(h => h.callback(event));
            }
        };
        this._touchMoveHandle = (e: DomEventObject) => {
            const event = setEventData(e);
            event.target._isTouch = false;
            this._pointMoveHandles.forEach(h => h.callback(event));
            if (event.touches!.length === 1) {
                this._leftPointMoveHandles.forEach(h => h.callback(event));
            } else if (event.touches!.length === 2) {
                this._rightPointMoveHandles.forEach(h => h.callback(event));
            }
        };
        this._touchEndHandle = (e: DomEventObject) => {
            const event = setEventData(e);
            this._pointUpHandles.forEach(h => h.callback(event));
            this._pointOutHandles.forEach(h => h.callback(event));
            let touchLength = event.touches!.length;
            if (touchLength === 2) {
                this._rightPointDownHandles.forEach(h => h.callback(event));
            } else if (touchLength === 1) {
                this._rightPointUpHandles.forEach(h => h.callback(event));
                this._leftPointDownHandles.forEach(h => h.callback(event));
            } else if (touchLength === 0) {
                this._leftPointUpHandles.forEach(h => h.callback(event));
            }
        };
        this._mouseOverHandle = (e: DomEventObject) => {
            const event = setEventData(e);
            this._pointEnterHandles.forEach(h => h.callback(event));
        };
        this._mouseMoveHandle = (e: DomEventObject) => {
            if (e.target._isTouch) {
                return;
            }
            const event = setEventData(e);
            this._pointMoveHandles.forEach(h => h.callback(event));
            if (event.buttons === 1) {
                this._leftPointMoveHandles.forEach(h => h.callback(event));
            } else if (event.buttons === 2) {
                this._rightPointMoveHandles.forEach(h => h.callback(event));
            }
        };
        this._mouseOutHandle = (e: DomEventObject) => {
            const event = setEventData(e);
            this._pointOutHandles.forEach(h => h.callback(event));
        };
        this._mouseDownHandle = (e: DomEventObject) => {
            if (e.target._isTouch) {
                return;
            }
            if (e.detail) {
                if (e.detail > 1) e.preventDefault();
            }

            const event = setEventData(e);
            this._pointDownHandles.forEach(h => h.callback(event));
            if (event.button === 0) {
                this._leftPointDownHandles.forEach(h => h.callback(event));
            } else if (event.button === 2) {
                this._rightPointDownHandles.forEach(h => h.callback(event));
            }
        };
        this._mouseUpHandle = (e: DomEventObject) => {
            if (e.target._isTouch) {
                return;
            }
            const event = setEventData(e);
            this._pointUpHandles.forEach(h => h.callback(event));
            if (event.button === 0) {
                this._leftPointUpHandles.forEach(h => h.callback(event));
            } else if (event.button === 2) {
                this._rightPointUpHandles.forEach(h => h.callback(event));
            }
        };
        this._clickHandle = (e: DomEventObject) => {
            const event = setEventData(e);
            event.target._isTouch = false;
            this._pointClickHandles.forEach(h => h.callback(event));
        };
        this._wheelHandle = (e: DomEventObject) => {
            e.isMouse = true;
            this._pointWheelHandles.forEach(h => h.callback(e));
        };
        this._windowBlurHandle = (e: DomEventObject) => {
            if (e.target === window) {
                this._windowBlurHandles.forEach(h => h.callback(e));
            }
        };
    }

}

export {
    DomEvent,
    DomEventObject
}